package com.cruel_wandering.wifi_geo_predictor;

import android.content.Intent;
import android.database.Cursor;
import android.graphics.Canvas;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;

public class MapScreen extends MapActivity {
	private static final String TAG = "Map Screen";
	private static final boolean DEBUG_MODE = true;
	private static final String MAP_API_KEY = "0gnb440wcg0nPfe4GKfgczHPRy5yutbq28lXILg";
	private static final String DEBUG_MAP_API_KEY = "0gnb440wcg0k_zk4OmR-G4GTR7bnRxF0xNscTug";
	private static final int million = 1000000;
	// maximal zoom level for google maps
	public static final double maxZoom = 22.0;
	// one click on zoom in/out scale up/down on factor zoomCoeff
	public static final double zoomCoeff = 2.0;
	// minimal length (in meters) that can be presented on screen
	public static final double minSpan = 10.0;
	// coeff. for 1 meter in degrees
	private static final double degCoeff = 0.000008993;

	private enum MyViewOption {
		VIEW_SATELLITE, VIEW_MAP, VIEW_STREET
	};

	private MapController mapController;
	private MapView mapView;
	private GeoPoint curPosition;
	private GeoPoint prevPosition;
	private float accuracy;
	private LocationListener listener = new MyLocationListener();
	private MyViewOption viewSelected;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);

		// create a map view
		mapView = new MapView(this, DEBUG_MODE ? DEBUG_MAP_API_KEY
				: MAP_API_KEY);
		mapController = mapView.getController();

		mapView.getController().setZoom(15);
		mapView.setClickable(true);
		mapView.setEnabled(true);
		mapView.setSatellite(true);
		viewSelected = MyViewOption.VIEW_SATELLITE;
		mapView.setBuiltInZoomControls(true);

		setContentView(mapView);

		Logger.getInstance(this).log(TAG, " created");
	}

	@Override
	protected void onStart() {
		super.onStart();

		Logger.getInstance(this).log(TAG, "MapScreen: registering a listener");
		Oracle.requestLocationUpdates(listener);
	}

	@Override
	protected void onStop() {
		Logger.getInstance(this).log(TAG, "MapScreen: unregistering a listener");
		Oracle.removeUpdates(listener);

		super.onStop();
	}

	public class MyLocationListener implements LocationListener {
		
		private static final String TAG = "TGPS-listener";

		public void onLocationChanged(Location loc) {
			int lat = (int) (loc.getLatitude() * 1E6);
			int lng = (int) (loc.getLongitude() * 1E6);
			curPosition = new GeoPoint(lat, lng);
			accuracy = loc.getAccuracy();
			Log.v(TAG, "Current position is : " + "(lng, lat) = (" + lng + ", "
					+ lat + ")");
			drawNewLocation();
			drawWifiZones();
		}

		public void onProviderDisabled(String provider) {
		}

		public void onProviderEnabled(String provider) {
		}

		public void onStatusChanged(String provider, int status, Bundle extras) {
		}
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}

	/**
	 * Enums for Menu options.
	 */
	private static class MenuEnums {
		public static final int MY_LOCATION = 1;
		public static final int SELECT_VIEW = 2;
		public static final int WIFI_ZONES = 3;

		public static final int SELECT_VIEW_GROUP_ID = 10;
		public static final int SELECT_MAP_VIEW = 11;
		public static final int SELECT_STREET_VIEW = 12;
		public static final int SELECT_SATELLITE_VEW = 13;

	}

	/**
	 * Create Menu options buttons.
	 */
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main_menu, menu);
		menu.removeItem(R.id.map_screen_menu);

		menu.add(0, MenuEnums.MY_LOCATION, 0, "My Location");
		menu.add(0, MenuEnums.WIFI_ZONES, 0, "WiFi Zones");
		SubMenu view = menu.addSubMenu(0, MenuEnums.SELECT_VIEW, 0,
				"Select View");

		view.add(MenuEnums.SELECT_VIEW_GROUP_ID, MenuEnums.SELECT_MAP_VIEW, 0,
				"Map").setChecked(viewSelected == MyViewOption.VIEW_MAP);
		view.add(MenuEnums.SELECT_VIEW_GROUP_ID,
				MenuEnums.SELECT_SATELLITE_VEW, 0, "Satellite").setChecked(
				viewSelected == MyViewOption.VIEW_SATELLITE);
		view.add(MenuEnums.SELECT_VIEW_GROUP_ID, MenuEnums.SELECT_STREET_VIEW,
				0, "Street").setChecked(
				viewSelected == MyViewOption.VIEW_STREET);
		view.setGroupCheckable(MenuEnums.SELECT_VIEW_GROUP_ID, true, true);

		return true;
	}

	/**
	 * Function that is used when map view options changed.
	 */
	private boolean onOptionsViewSelected(MenuItem item) {
		switch (item.getItemId()) {
		case MenuEnums.SELECT_MAP_VIEW:
		case MenuEnums.SELECT_SATELLITE_VEW:
		case MenuEnums.SELECT_STREET_VIEW:
			if (item.isChecked())
				item.setChecked(false);
			else
				item.setChecked(true);
		}

		switch (item.getItemId()) {
		case MenuEnums.SELECT_MAP_VIEW:
			mapView.setStreetView(false);
			mapView.setSatellite(false);
			setContentView(mapView);
			return true;
		case MenuEnums.SELECT_SATELLITE_VEW:
			mapView.setSatellite(true);
			mapView.setStreetView(false);
			setContentView(mapView);
			return true;
		case MenuEnums.SELECT_STREET_VIEW:
			mapView.setStreetView(true);
			mapView.setSatellite(false);
			setContentView(mapView);
			return true;
		}
		return false;
	}

	/**
	 * Function that is used when map Item view options changed.
	 */
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getGroupId()) {
		case MenuEnums.SELECT_VIEW_GROUP_ID:
			return onOptionsViewSelected(item);
		}

		switch (item.getItemId()) {
		case MenuEnums.MY_LOCATION:
			if (curPosition == null) {
				Location tmp = Oracle.getLastLocation();
				if (tmp != null){
					int lat = (int) (tmp.getLatitude() * 1E6);
					int lng = (int) (tmp.getLongitude() * 1E6);
					curPosition = new GeoPoint(lat, lng);
					accuracy = tmp.getAccuracy();
					drawNewLocation();
					return true;
				}
				Toast t = Toast.makeText(getApplicationContext(),
						"Waiting for location", Toast.LENGTH_LONG);
				t.setGravity(Gravity.BOTTOM, 0, 0);
				t.show();
			}
			else
				drawNewLocation();
			return true;

		case MenuEnums.WIFI_ZONES:
			drawWifiZones();
			return true;

		case R.id.preferences:
			startActivity(new Intent(MapScreen.this, Preferences.class));
			return true;
			/**
			 * Function that is used when map view options changed.
			 */
		case R.id.db_screen_menu:
			startActivity(new Intent(MapScreen.this, DBScreen.class));
			return true;
		}
		return false;
	}

	/**
	 * Function that create new my location point overlay.
	 */
	public void addNewMyLocationPointToMapView() {
		DisplayMetrics dm = new DisplayMetrics();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		Canvas canvas = new Canvas();
		MyLocationOverlay overlay = new MyLocationOverlay(curPosition
				.getLatitudeE6(), curPosition.getLongitudeE6(), accuracy, dm,
				this);
		overlay.draw(canvas, mapView, true);
		mapView.getOverlays().add(overlay);
	}

	/**
	 * Function that draws my location point.
	 */
	public void drawNewLocation() {
		Logger.getInstance(this).log(
				"MAP",
				"Location chaged to (lng, lat) = ("
						+ curPosition.getLatitudeE6() + ", "
						+ curPosition.getLongitudeE6() + ")");
		if (prevPosition != null) {
			if (prevPosition.getLatitudeE6() != curPosition.getLatitudeE6()
					|| prevPosition.getLongitudeE6() != curPosition
							.getLongitudeE6()) {
				prevPosition = new GeoPoint(curPosition.getLatitudeE6(),
						curPosition.getLongitudeE6());
				mapView.getOverlays().clear();
				setContentView(mapView);
				addNewMyLocationPointToMapView();
			}
		} else {
			prevPosition = new GeoPoint(curPosition.getLatitudeE6(),
					curPosition.getLongitudeE6());
			addNewMyLocationPointToMapView();
		}
		setContentView(mapView);
		if (curPosition != null)
			mapController.animateTo(curPosition);
	}

	/**
	 * Function that draws known wifi zones.
	 */
	public void drawWifiZones() {
		DataBase db = DataBase.getInstance(this);
		Cursor cursor = null;
		double lat = mapView.getMapCenter().getLongitudeE6() / million;
		double lon = mapView.getMapCenter().getLongitudeE6() / million;
		double zoom = maxZoom - mapView.getZoomLevel();
		double scrLengthMeter = minSpan * Math.pow(zoomCoeff, zoom);
		double scrLengthDeg = scrLengthMeter * degCoeff;

		/**
		Logger.getInstance(this).log(
				"MAP",
				"small lat : "
						+ ((Double) (lat - (scrLengthDeg / 2))).toString());
		Logger.getInstance(this)
				.log(
						"MAP",
						"big lat : "
								+ ((Double) (lat + (scrLengthDeg / 2)))
										.toString());
		Logger.getInstance(this).log(
				"MAP",
				"small lng : "
						+ ((Double) (lon - (scrLengthDeg / 2))).toString());
		Logger.getInstance(this)
				.log(
						"MAP",
						"big lng : "
								+ ((Double) (lon + (scrLengthDeg / 2)))
										.toString());
		Logger.getInstance(this).log("MAP",
				"lat : " + ((Double) (lat)).toString());
		Logger.getInstance(this).log("MAP",
				"lng : " + ((Double) (lon)).toString());
		Logger.getInstance(this).log("MAP",
				"scrLengthDeg : " + ((Double) scrLengthDeg).toString());
		Logger.getInstance(this).log("MAP",
				"scrLengthMeter : " + ((Double) scrLengthMeter).toString());
				**/

		DisplayMetrics dm = new DisplayMetrics();
		Canvas canvas = new Canvas();
		getWindowManager().getDefaultDisplay().getMetrics(dm);
		int i = 0;
		try {
			db.open();
			cursor = db.getAllTitlesInRange(lat - (scrLengthDeg / 2), lat
					+ (scrLengthDeg / 2), lon - (scrLengthDeg / 2), lon
					+ (scrLengthDeg / 2));
			if (cursor != null && cursor.moveToFirst()) {
				do {
					i++;
					double pointLat = cursor.getDouble(3);
					double pointLng = cursor.getDouble(4);
					float radius = (float)cursor.getInt(2);
					MyWiFiOverlay overlay = new MyWiFiOverlay(pointLat,
							pointLng, radius, dm, this);
					overlay.draw(canvas, mapView, true);
					mapView.getOverlays().add(overlay);
				} while (cursor.moveToNext());
			}
			mapView.draw(canvas);
		} catch (Exception e) {
			Logger.getInstance(this).log("MAP",
					"There are no any record in the data Base !");
			Toast.makeText(getApplicationContext(),
					"There are no WiFi zones in the presented area!",
					Toast.LENGTH_LONG).show();
		} finally {
			Logger.getInstance(this).log("MAP",
				"Map Screen. drawWifiZones.  finalizer. "+Integer.toString(i)+" zones Were printed.");
			if (cursor != null && !cursor.isClosed())
				cursor.close();
			if (db.getDatabase() != null && db.getDatabase().isOpen())
				db.getDatabase().close();
		}
		setContentView(mapView);
	}

}
